package com.xkw.autocode.util;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.Transient;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import tk.mybatis.mapper.entity.Example;

/**
 * 获取Example对象的工具类
 * @author xiangkaiwei
 */
public class ExampleUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(ExampleUtils.class);
	
	private static final String KEY_BETWEEN = "Between";
	private static final String KEY_INLIST = "InList";
	private static final String KEY_STARTSWITH = "StartsWith";
	private static final String KEY_ENDSWITH = "EndsWith";
	private static final String KEY_ANDOR = "_AndOr";
	private static final String KEY_LENGTH = "Length";
	
	
	private static final String KEY_GT = "GreaterThan";
	private static final String KEY_EGT = "EqualGreaterThan";
	private static final String KEY_LT = "LessThan";
	private static final String KEY_ELT = "EqualLessThan";
	
	private ExampleUtils() {
		super();
	}
	private static final String REX = "([A-Z])";
	
	/**
	 * 通过一个对象获取对应的Example对象
	 * @param object	源对象
	 * @return
	 * @throws Exception
	 */
	public static Example getExampleFromObject(Object object) {
		return getExampleFromObject(object, null);
	}
	
	/**
	 * 通过一个对象获取对应的Example对象
	 * @param object	源对象
	 * @param withOutProperties	排除哪些属性不处理
	 * @return
	 * @throws Exception
	 */
	public static Example getExampleFromObject(Object object,Set<String> withOutProperties){
		if(object == null){
			return null;
		}

		Example example = new Example(object.getClass());
		
		Example.Criteria criteria = example.createCriteria();
		Example.Criteria criteriaAnd = example.createCriteria();
		
		//获取对象的方法
		Method[] methods = getMethods(object.getClass());
		
		//循环设置对象
		for (int i = 0; i < methods.length; i++) {
			
			Method method = methods[i];
			
			//获取属性值
			Object value = getValue(object, method, withOutProperties);
			if(value == null) {
				continue;
			}
			
			String methodName = method.getName();
			String fieldName = getFieldName(methodName);
			
			//设置条件
			setCriteria(fieldName, value, criteria, criteriaAnd);
			
		}
		if(criteriaAnd.getCriteria() == null || criteriaAnd.getCriteria().isEmpty()){
			return example;
		}
		example.and(criteriaAnd);

		return example;
	}
	
	private static String getFieldName(String methodName){
		String fieldName = methodName.substring(3);
		return fieldName.substring(0,1).toLowerCase() + fieldName.substring(1);
	}
	
	/**
	 * 获取属性值
	 * @param object
	 * @param method
	 * @param withOutProperties
	 * @return
	 */
	private static Object getValue(Object object,Method method,Set<String> withOutProperties) {
		
		String methodName = method.getName();
		
		//判断源对象是否有 get 方法
		if (!methodName.startsWith("get")){
			return null;
		}
		
		String fieldName = getFieldName(methodName);
		
		//排除不需要复制的属性
		if(withOutProperties != null && withOutProperties.contains(fieldName)){
			return null;
		}
		
		//获取原对象中的值
		Object value = null;
		try {
			value = method.invoke(object);
		} catch (Exception e) {
			logger.error("",e);
		}
		
		//判断值是否为空，为空则跳过
		if (value == null){
			return null;
		}
		
		Transient t = method.getAnnotation(Transient.class);
		if(t != null) {
			return null;
		}
		
		if(value instanceof String){
			if(String.valueOf(value).length() == 0){
				return null;
			}
		}else if (value instanceof Collection && !fieldName.endsWith(KEY_INLIST)) { // 跳过集合类型
			return null;
		}
		return value;
	}
	
	/**
	 * 设置条件
	 */
	private static void setCriteria(String fieldName,Object value,Example.Criteria criteria,Example.Criteria criteriaAnd) {
		//处理时间，例如：createTimeBetween=2018/10/04 - 2018/10/31
		if(fieldName.endsWith(KEY_BETWEEN)) {
			fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_BETWEEN));
			
			Long[] times = MyDateUtils.getStartAndEndTime(String.valueOf(value));
			if(times != null) {
				criteria.andBetween(fieldName, times[0], times[1]);
			}
		}else if(fieldName.endsWith(KEY_GT)) {	//大于
			fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_GT));
			criteria.andGreaterThan(fieldName, value);
		}else if(fieldName.endsWith(KEY_EGT)) {		//大于等于
			fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_EGT));
			criteria.andGreaterThanOrEqualTo(fieldName, value);
		}else if(fieldName.endsWith(KEY_LT)) {		//小于
			fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_LT));
			criteria.andLessThan(fieldName, value);
		}else if(fieldName.endsWith(KEY_ELT)) {	//小于等于
			fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_ELT));
			criteria.andLessThanOrEqualTo(fieldName, value);
		}else if(fieldName.endsWith(KEY_INLIST)) {
			setInlistCriteria(fieldName, value, criteriaAnd);
		}else if(fieldName.endsWith(KEY_LENGTH)) {
			fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_LENGTH));
			criteria.andCondition(String.format("length(%s)=", transferCase(fieldName)), value);
			
		}else if(fieldName.endsWith(KEY_ANDOR)) {
			setAndorCriteria(fieldName, value, criteriaAnd);
		}else {
			setCommonCriteria(fieldName, value, criteriaAnd);
		}
	}
	
	private static void setInlistCriteria(String fieldName,Object value,Example.Criteria criteria) {
		fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_INLIST));
		Collection<?> c = (Collection<?>)value;
		
		if(fieldName.endsWith(KEY_STARTSWITH)) {
			fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_STARTSWITH));
			StringBuilder condition = new StringBuilder();
			String sqlField = transferCase(fieldName);
			for (Object obj : c) {
				String valString = obj == null ? null : String.valueOf(obj);
				if (valString != null && valString.length() > 0) {
					condition.append(String.format("or instr(%s, '%s') = 1 ", sqlField, valString));
				}
			}
			if (condition.length() > 3) {
				criteria.andCondition("(" + condition.substring(3) + ")");
			}
		} else {
			criteria.andIn(fieldName, c);
		}
	}
	
	private static void setAndorCriteria(String fieldName,Object value,Example.Criteria criteriaAnd) {
		fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_ANDOR));
		String[] fieldNames = fieldName.split(KEY_ANDOR);
		if(fieldNames != null){
			for (String fname : fieldNames) {
				criteriaAnd.orCondition(String.format("instr(%s, '%s') > 0", transferCase(fname), String.valueOf(value)));
			}
		}
	}
	
	private static void setCommonCriteria(String fieldName,Object value,Example.Criteria criteria) {
		if(value instanceof String) {
			if(fieldName.endsWith(KEY_STARTSWITH)){
				fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_STARTSWITH));
				criteria.andLike(fieldName, value + "%");
			}else if(fieldName.endsWith(KEY_ENDSWITH)){
				fieldName = fieldName.substring(0,fieldName.lastIndexOf(KEY_ENDSWITH));
				criteria.andLike(fieldName, "%" + value);
			}else{
				criteria.andLike(fieldName, "%"+ value +"%");
			}
		}else {
			criteria.andEqualTo(fieldName, value);
		}
	}
	
	/**
	 * 通过map获取Example对象
	 * @param c
	 * @param map
	 * @return
	 */
	public static Example getExampleFromMap(Class<?> c,Map<String,Object> map){
		if(map == null || map.size() == 0){
			return null;
		}

		Example example = new Example(c);
		
		Example.Criteria criteria = example.createCriteria();
		
		//循环设置对象
		for(Map.Entry<String, Object> entry : map.entrySet()) {
			
			Object value = entry.getKey();
			if(value instanceof String) {
				criteria.andLike(entry.getKey(), "%"+ value +"%");
			}else {
				criteria.andEqualTo(entry.getKey(), value);
			}
		}

		return example;
	}
	
	/**
	 * 获取类的所有方法
	 */
	private static Method[] getMethods(Class<?> c){
		
		//当前类的所有方法
		Method[] methods = c.getDeclaredMethods();
		
		Class<?> superClass = c.getSuperclass();
		if(superClass != null){
			Method[] superMethods = superClass.getDeclaredMethods();
			if(superMethods != null && superMethods.length > 0){
				return ArrayUtils.addAll(superMethods, methods);
			}
		}
		return methods;
	}
	
	/**
	 * 驼峰改成下划线
	 * @param sourceStr
	 * @return
	 */
	private static String transferCase(String sourceStr) {
		
		if(sourceStr == null || sourceStr.trim().length() == 0) {
			return sourceStr;
		}
		
		Pattern ptn = Pattern.compile(REX);
		Matcher m = ptn.matcher(sourceStr);
		
		StringBuilder sb = new StringBuilder(sourceStr);
		int position = 0;
		int count = 0;
		while(m.find(position)) {
			sb.replace(m.start() + count, m.end() + count, "_" + m.group(1).substring(0, 1).toLowerCase());
			position = m.end();
			count++;
		}
		
		return sb.toString();
	}
}